﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.AudioVideo;
using Microsoft.Rtc.Collaboration.Presence;
using Microsoft.Rtc.Signaling;
using System.Threading;
using Microsoft.Rtc.Collaboration.Samples.HTMLPresenceControls.Service.Common;

namespace Microsoft.Rtc.Collaboration.Samples.HTMLPresenceControls.Service
{
    /// <summary>
    /// The default UCMA implementation of IPresenceCache.
    /// Gathers and caches presence updates for the requested sip uri(s)
    /// </summary>
    public class UcPresenceCache
    {
        private Dictionary<string, PresenceSubscription> m_presenceSubscriptionsBySipUri = 
            new Dictionary<string, PresenceSubscription>(StringComparer.CurrentCultureIgnoreCase);
        private object m_syncObject = new object();
        private Timer _staleTimer;
        private DateTime StaleTime { get { return DateTime.Now - StaleInterval; } }

        private IPresenceProvider m_presenceProvider;

        /// <summary>
        /// The interval to run cache cleanup. If any presence subscriptions have not been requested in the last stale interval
        /// they will be removed from the cache
        /// </summary>
        public TimeSpan StaleInterval { get; set; }

        public UcPresenceCache(IPresenceProvider provider)
        {
            m_presenceProvider = provider;
            m_presenceProvider.PresenceChanged += new EventHandler<PresenceChangedEventArgs>(PresenceProvider_PresenceChanged);
            StaleInterval = TimeSpan.FromHours(2);
            _staleTimer = new Timer(OnStaleTimerTick, null, StaleInterval, TimeSpan.FromMilliseconds(-1));
        }

        public PresenceSubscription GetPresence(string sipUri)
        {
            lock (m_syncObject)
            {
                if (!m_presenceSubscriptionsBySipUri.ContainsKey(sipUri))
                {
                    m_presenceSubscriptionsBySipUri.Add(sipUri, new PresenceSubscription
                    {
                        Availability = (long)PresenceAvailability.None,
                        ContactName = "Unknown",
                        SipUri = sipUri,
                        Status = "Unknown",
                        SubscriptionCreated = DateTime.Now
                    });
                    m_presenceProvider.SubscribeToPresence(sipUri);
                }
                //update the timestamp whenever the presence is requested
                //this prevents a presence from being cleared out of the cache at the StaleInterval
                m_presenceSubscriptionsBySipUri[sipUri].SubscriptionCreated = DateTime.Now;
                return m_presenceSubscriptionsBySipUri[sipUri];
            }
        }

        public List<PresenceSubscription> GetPresence(List<string> sipUris)
        {
            List<PresenceSubscription> presences = new List<PresenceSubscription>();
            foreach (string sipUri in sipUris)
            {
                presences.Add(GetPresence(sipUri));
            }
            return presences;
        }

        #region Manage Presence Changes
        private void PresenceProvider_PresenceChanged(object sender, PresenceChangedEventArgs e)
        {
            lock (m_syncObject)
            {
                foreach (PresenceSubscription presence in e.Presences)
                {
                    if (m_presenceSubscriptionsBySipUri.ContainsKey(presence.SipUri))
                    {
                        UpdatePresenceSubscriptionWithChanges(presence);
                    }
                    else //received a presence changed event for an expired subscription
                    {
                        Helper.Logger.Info("Ignoring presence update for sipUri {0} since there are no active subscriptions for this uri", presence.SipUri);
                    }
                }
            }
        }

        private void UpdatePresenceSubscriptionWithChanges(PresenceSubscription presence)
        {
            if (presence.Availability != PresenceSubscription.NoChangeInAvailability)
            {
                m_presenceSubscriptionsBySipUri[presence.SipUri].Availability = presence.Availability;
            }
            if (presence.ContactName != PresenceSubscription.NoChange)
            {
                m_presenceSubscriptionsBySipUri[presence.SipUri].ContactName = presence.ContactName;
            }
            if (presence.Status != PresenceSubscription.NoChange)
            {
                m_presenceSubscriptionsBySipUri[presence.SipUri].Status = presence.Status;
            }
        }
        #endregion

        #region Cache Cleanup
        private void RemoveStaleSubscriptions()
        {
            lock (m_syncObject)
            {
                DateTime staleTime = StaleTime;
                var staleKvps = m_presenceSubscriptionsBySipUri.Where(kvp => kvp.Value.SubscriptionCreated <= StaleTime).ToList();
                foreach (var staleKvp in staleKvps)
                {
                    string sipUri = staleKvp.Key;
                    Helper.Logger.Info("Removing stale subscription for sip uri {0}, last requested at {1}", sipUri, staleKvp.Value.SubscriptionCreated);
                    m_presenceProvider.UnsubscribeToPresence(sipUri);
                    m_presenceSubscriptionsBySipUri.Remove(sipUri);
                }
            }
        }

        private void OnStaleTimerTick(object state)
        {
            //stop the stale timer
            _staleTimer.Change(TimeSpan.FromMilliseconds(-1), TimeSpan.FromMilliseconds(-1));
            RemoveStaleSubscriptions();
            _staleTimer.Change(StaleInterval, TimeSpan.FromMilliseconds(-1));
        }
        #endregion


       
    }
}

 